/**
 * Copyright 2019 吉鼎科技.

 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.easyplatform.services.system;

import cn.easyplatform.ServiceException;
import cn.easyplatform.entities.beans.ResourceBean;
import cn.easyplatform.i18n.I18N;
import cn.easyplatform.lang.Mirror;
import cn.easyplatform.services.AbstractService;
import cn.easyplatform.services.IDataSource;
import cn.easyplatform.services.IProjectService;
import cn.easyplatform.type.StateType;
import cn.easyplatform.util.ConfigTools;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.stat.JdbcSqlStat;
import org.slf4j.LoggerFactory;

import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;

/**
 * @author <a href="mailto:davidchen@epclouds.com">littleDog</a> <br/>
 * @since 2.0.0 <br/>
 */
public class DataSourceService extends AbstractService implements IDataSource {

    private static final org.slf4j.Logger log = LoggerFactory.getLogger(DataSourceService.class);

    private ResourceBean bean;

    private DruidDataSource ds;

    private Date startTime;

    private IProjectService ps;

    /**
     * @param bean
     */
    public DataSourceService(IProjectService ps, ResourceBean bean) {
        this.ps = ps;
        this.bean = bean;
    }

    @Override
    public String getId() {
        return bean.getId();
    }

    @Override
    public void start() throws ServiceException {
        if (getState() != StateType.STOP)
            return;
        ds = new DruidDataSource();
        // ds.setName(bean.getId());
        if (log.isDebugEnabled())
            log.debug(I18N.getLabel("easyplatform.sys.common.starting", bean.getName()));
        Set<String> em = bean.getProps().keySet();
        try {
            Mirror<DruidDataSource> me = Mirror.me(DruidDataSource.class);
            for (String methodName : em) {
                if (!methodName.equals("master") && !methodName.equals("decrypt")) {
                    if (methodName.equals("password") && "true".equals(bean.getProps().get("decrypt"))) {//用户密码不以明文显示
                        me.getInjecting(methodName).inject(ds, ConfigTools.decrypt(bean.getProps().get(methodName).trim()));
                    } else
                        me.getInjecting(methodName).inject(ds, bean.getProps().get(methodName).trim());
                }
            }
            ds.setResetStatEnable(true);
            ds.init();
            if (ps == null)//参数数据库
                engineConfiguration.addService(this);
            else
                ps.addService(this);
            setState(StateType.START);
            startTime = new Date();
            if (log.isDebugEnabled())
                log.debug(I18N.getLabel("easyplatform.sys.common.started", bean.getName()));
        } catch (Exception ex) {
            ex.printStackTrace();
            try {
                ds.close();
            } catch (Exception e) {
            }
            if (ex instanceof ServiceException)
                throw (ServiceException) ex;
            throw new ServiceException(I18N.getLabel("easyplatform.sys.common.fail", getName(), ex.getMessage()));
        }
    }

    @Override
    public void stop() throws ServiceException {
        if (getState() == StateType.STOP)
            return;
        if (log.isDebugEnabled())
            log.debug(I18N.getLabel("easyplatform.sys.common.stopping", bean.getName()));
        try {
            if (ds != null)
                ds.close();
            if (ps == null)
                engineConfiguration.removeService(this);
            else
                ps.removeService(this.getId());
            setState(StateType.STOP);
            startTime = null;
            if (log.isDebugEnabled())
                log.debug(I18N.getLabel("easyplatform.sys.common.stopped", bean.getName()));
        } catch (Exception ex) {
            if (log.isWarnEnabled())
                log.warn(I18N.getLabel("easyplatform.sys.common.stop.fail", getName(), ex.getMessage()));
        }
    }

    @Override
    public String getName() {
        return bean.getName();
    }

    @Override
    public String getDescription() {
        return bean.getDescription();
    }

    @Override
    public Map<String, Object> getRuntimeInfo() {
        Map<String, Object> rt = new HashMap<String, Object>();
        rt.put("basic", ds.getStatDataForMBean());
        try {
            Map<String, Map<String, Object>> sqlStatMap = new HashMap<>();
            Map<String, JdbcSqlStat> data = ds.getSqlStatMap();
            for (Map.Entry<String, JdbcSqlStat> entry : data.entrySet()) {
                Map<String, Object> detail = new HashMap<>(13);
                sqlStatMap.put(entry.getKey(), detail);
                Map<String, Object> map = entry.getValue().getData();
                Object val = map.get("ExecuteCount");
                detail.put("ExecuteCount", val == null ? "0" : val);
                val = map.get("ExecuteTimeMillis");
                detail.put("ExecuteTimeMillis", val == null ? "0" : val);
                val = map.get("MaxTimespan");
                detail.put("MaxTimespan", val == null ? "0" : val);
                val = map.get("InTransactionCount");
                detail.put("InTransactionCount", val == null ? "0" : val);
                val = map.get("ErrorCount");
                detail.put("ErrorCount", val == null ? "0" : val);
                val = map.get("JdbcUpdateCount");
                detail.put("JdbcUpdateCount", val == null ? "0" : val);
                val = map.get("FetchRowCount");
                detail.put("FetchRowCount", val == null ? "0" : val);
                val = map.get("RunningCount");
                detail.put("RunningCount", val == null ? "0" : val);
                val = map.get("ConcurrentMax");
                detail.put("ConcurrentMax", val == null ? "0" : val);
                detail.put("ExecHisto", map.get("ExecHisto"));
                detail.put("ExecRsHisto", map.get("ExecRsHisto"));
                detail.put("FetchRowHisto", map.get("FetchRowHisto"));
                detail.put("UpdateHisto", map.get("UpdateHisto"));
            }
            rt.put("sql", sqlStatMap);
        } catch (Exception e) {
        }
        rt.put("pool", ds.getPoolingConnectionInfo());
        return rt;
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.sql.CommonDataSource#getLogWriter()
     */
    @Override
    public PrintWriter getLogWriter() throws SQLException {
        return ds.getLogWriter();
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.sql.CommonDataSource#setLogWriter(java.io.PrintWriter)
     */
    @Override
    public void setLogWriter(PrintWriter out) throws SQLException {
        ds.setLogWriter(out);
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.sql.CommonDataSource#setLoginTimeout(int)
     */
    @Override
    public void setLoginTimeout(int seconds) throws SQLException {
        ds.setLoginTimeout(seconds);
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.sql.CommonDataSource#getLoginTimeout()
     */
    @Override
    public int getLoginTimeout() throws SQLException {
        return ds.getLoginTimeout();
    }

    /*
     * (non-Javadoc)
     *
     * @see java.sql.Wrapper#unwrap(java.lang.Class)
     */
    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        return ds.unwrap(iface);
    }

    /*
     * (non-Javadoc)
     *
     * @see java.sql.Wrapper#isWrapperFor(java.lang.Class)
     */
    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return ds.isWrapperFor(iface);
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.sql.DataSource#getConnection()
     */
    @Override
    public Connection getConnection() throws SQLException {
        return ds.getConnection();
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.sql.DataSource#getConnection(java.lang.String,
     * java.lang.String)
     */
    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        return ds.getConnection(username, password);
    }

    @Override
    public Date getStartTime() {
        return startTime;
    }

    /**
     * @return
     */
    public ResourceBean getEntity() {
        return bean;
    }

    @Override
    public String toString() {
        // dao通过这个可以判断数据库的类型：mysql,sqlserver,oracle,db2,derby等等
        return bean.getProps().get("jdbcUrl");
    }

    @Override
    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
        return ds.getParentLogger();
    }

    @Override
    public void resetSqlStat(Long id) {
        if (id != null && id > 0) {
            ds.getSqlStat(id).reset();
        } else {
            for (JdbcSqlStat stat : ds.getSqlStatMap().values())
                stat.reset();
        }
    }

    @Override
    public void resetDataSourceStat() {
        ds.getDataSourceStat().reset();
    }
}
